package site.param.init;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.api.CuratorWatcher;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import site.param.remote.IParameterRemoteService;
import site.param.spring.ParameterInitBean;

import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;

/**
 * 参数管理 客户端初始化类
 */
public class ClientLoadParamter {
    private static Logger logger = LoggerFactory.getLogger(ClientLoadParamter.class);
    private CuratorFramework zkTools;
    private ConcurrentSkipListSet watchers = new ConcurrentSkipListSet();
    private static Charset charset = Charset.forName("utf-8");
    private Map<String, ParameterMap> parameterTable = null;
    private Set<String> sets = null;
    private Map<String, Class> classMap = null;
    private static final String zkRootNode = "paramter";
    private ParameterInitBean parameterInitBean;

    private IParameterRemoteService parameterRemoteService;

    public ClientLoadParamter(ParameterInitBean parameterInitBean) {
        logger.info("ClientLoadParamter init ,parameterInitBean is :{}", parameterInitBean == null);
        if (parameterInitBean != null) {
            this.parameterInitBean = parameterInitBean;
            initMethod();
        }

    }

    // 注册到节点
    private void register() throws Exception {

        // 添加到session过期监控事件中
        // addReconnectionWatcher(registeNode, ZookeeperWatcherType.CREATE_ON_NO_EXITS,watcher);
        for (String str : sets) {
            ZKWatchRegister zkWatchRegister = new ZKWatchRegister(str, str.getBytes());
            zkTools.checkExists().usingWatcher(zkWatchRegister).forPath(str);
        }
        logger.info("register zkhost:{}, node size:{}", parameterInitBean.getZookeepHost(), sets.size());
    }

    /**
     * 初始化方法
     */
    private void initMethod() {
        parameterRemoteService = parameterInitBean.getBean(IParameterRemoteService.class);
        classMap = ParameterInterpret.initClassMap(parameterInitBean.getScanPackage());
        parameterTable = ParameterInterpret.interpret(classMap);
        sets = new HashSet<String>();
        for (String key : parameterTable.keySet()) {
            sets.add("/" + parameterTable.get(key).getSerialUID());
        }
        try {

           /* IParameterHttpService iParameterHttpService=new IParameterHttpService();
            iParameterHttpService.initParamter(parameterTable);*/
            Map<String, ParameterMap> parameterMapMaps = parameterRemoteService.initParamter(parameterTable);

            for (String uid : parameterMapMaps.keySet()) {
                ParameterInterpret.setParamter(classMap.get(uid), parameterMapMaps.get(uid));
            }
            zkTools = CuratorFrameworkFactory.builder().connectString(parameterInitBean.getZookeepHost()).namespace(zkRootNode)
                    .retryPolicy(new RetryNTimes(parameterInitBean.getRetryCount() > 0 ? parameterInitBean.getRetryCount() : 5,
                            parameterInitBean.getElapsedTimeMs() > 5000 ? parameterInitBean.getElapsedTimeMs() : 5000))
                    .build();
            zkTools.start();
            register();
        } catch (Exception e) {
            logger.info("init parameter error:{}",e.getLocalizedMessage());
        }
    }

    private void loadParameter(String path) {
        if (sets.contains(path)) {
            String classId = ParameterUtil.getClassId(path);
            try {
                ParameterInterpret.setParamter(classMap.get(classId), parameterRemoteService.reloadParamter(parameterTable.get(classId)));
            } catch (Exception e) {
                logger.error("loadParameter  error:{}",e.getLocalizedMessage());
            }
        }
    }

    public class ZKWatchRegister implements CuratorWatcher {
        private final String path;
        private byte[] value;

        public String getPath() {
            return path;
        }

        public ZKWatchRegister(String path, byte[] value) {
            this.path = path;
            this.value = value;
        }

        @Override
        public void process(WatchedEvent event) throws Exception {
            System.out.println(event.getType());
            if (event.getType() == Watcher.Event.EventType.NodeDataChanged) {
                loadParameter(path);
                // 节点数据改变了，需要记录下来，以便session过期后，能够恢复到先前的数据状态
                // 循环监听
                byte[] data = zkTools.getData().usingWatcher(this).forPath(path);
                value = data;
            }
        }
    }

}